package org.abettor.util.buffer;

import java.lang.reflect.Array;

public class CycleQueue<T>
{
	private static final int DEF_INIT_SIZE = 1024;
	private static final int DEF_MAX_SIZE = 1024 * 1024;
	private static final int DEF_INC_SIZE = 1024;
	private Class<T> clazz = null;
	private int initSize = DEF_INIT_SIZE;
	private int maxSize = DEF_MAX_SIZE;
	private int incSize = DEF_INC_SIZE;
	private int startIdx = 0;
	private int endIdx = 0;//endIdx处是没有数据的！！！！！！
	private boolean full = false;//startIdx==endIdx时有可能是空队列，也有可能是满队列，用full区分
	private T[] buffer = null;
	private int bufferSize = 0;

	/**
	 * 构造环状数据队列
	 * @param clazz 实际存储的对象的类对象
	 * @param initSize 初始大小
	 * @param maxSize 最大配额
	 * @param incSize 每次增量大小
	 */
	public CycleQueue(Class<T> clazz, int initSize, int maxSize, int incSize)
	{
		this.clazz = clazz;
		if(initSize > 0)
			this.initSize = initSize;
		if(maxSize > 0)
			this.maxSize = maxSize;
		if(incSize > 0)
			this.incSize = incSize;

		this.reset();
	}

	/**
	 * 在“尾部”追加数据
	 * @param data 要追加的数据
	 * @return 如果已经超过最大允许容量，则拒绝追加，同时返回false，否则返回true
	 */
	@SuppressWarnings("unchecked")
	public synchronized boolean append(T[] data)
	{
		int currentSize = this.size();
		int dataLen = data.length;

		//	超出了最大控件限制的情况
		if(dataLen + currentSize > maxSize)
		{
			return false;
		}
		//	当前空闲空间足以容纳新增数据的情况
		else if(dataLen <= bufferSize - currentSize)
		{
			//	空闲空间连续
			if(endIdx < startIdx)
			{
				System.arraycopy(data, 0, buffer, endIdx, dataLen);
				endIdx += dataLen;
			}
			//	空闲空间不连续，但是“尾”之后的连续控件足够容纳新增数据
			else if(endIdx + dataLen <= bufferSize)
			{
				System.arraycopy(data, 0, buffer, endIdx, dataLen);
				endIdx += dataLen;
			}
			//	分段存储的情况
			else
			{
				int frag1Len = bufferSize - endIdx;
				int frag2Len = dataLen - frag1Len;
				System.arraycopy(data, 0, buffer, endIdx, frag1Len);
				System.arraycopy(data, frag1Len, buffer, 0, frag2Len);
				endIdx = frag2Len;
			}

			//	检查是不是满了
			if(endIdx == bufferSize)
			{
				endIdx = 0;
			}
			if(startIdx == endIdx)
			{
				full = true;
			}
		}
		//	通过扩展空间达到存储新数据的情况
		else
		{
			//	计算空间需求（新数据长度-空闲长度）圆整到incSize的整数倍
			int freeSize = bufferSize - currentSize;
			int needSize = (dataLen - freeSize) / incSize * incSize;
			if((dataLen - freeSize) % incSize > 0)
			{
				needSize += incSize;
			}
			int newSize = bufferSize + needSize;

			//	首先创建新的存储空间，并将原有数据转移到新空间
			T[] tmp = buffer;
			int tmpStart = startIdx;
			int tmpEnd = endIdx;
			int tmpBufferSize = bufferSize;
			boolean tmpFull = full;
			buffer = (T[]) Array.newInstance(clazz, newSize);
			bufferSize = newSize;
			startIdx = 0;
			endIdx = 0;
			full = false;
			//	连续空间的情况
			if(tmpStart < tmpEnd)
			{
				System.arraycopy(tmp, tmpStart, buffer, 0, tmpEnd - tmpStart);
				endIdx = tmpEnd - tmpStart;
			}
			//	分片存储的情况
			else if(tmpStart > tmpEnd)
			{
				int fragLen = tmpBufferSize - tmpStart;
				System.arraycopy(tmp, tmpStart, buffer, 0, fragLen);
				System.arraycopy(tmp, 0, buffer, fragLen, tmpEnd);
				endIdx = fragLen + tmpEnd;
			}
			//	分片且队列满了的情况
			else if(tmpFull)
			{
				int fragLen = tmpBufferSize - tmpStart;
				System.arraycopy(tmp, tmpStart, buffer, 0, fragLen);
				System.arraycopy(tmp, 0, buffer, fragLen, tmpEnd);
				endIdx = fragLen + tmpEnd;
			}

			//	以递归的方式追加数据
			System.gc();
			return this.append(data);
		}

		return true;
	}

	/**
	 * 取数据
	 * @param len 计划读取的长度
	 * @param remove 是否从队列中删除已读取的数据
	 * @return 实际读取的数据信息
	 */
	@SuppressWarnings("unchecked")
	public synchronized T[] peek(int len, boolean remove)
	{
		//	检查是否可读
		int currentSize = this.size();
		if(currentSize == 0 || len <= 0)
		{
			return null;
		}

		//	计算实际读取长度
		int realLen = len < currentSize ? len : currentSize;
		T[] result = (T[]) Array.newInstance(clazz, realLen);

		//	连续存储空间的情况
		if(startIdx < endIdx)
		{
			System.arraycopy(buffer, startIdx, result, 0, realLen);
			if(remove)
			{
				startIdx += realLen;
			}
		}
		//	非连续存储，但“头”之后的数据足以满足要求
		else if(bufferSize >= startIdx + realLen)
		{
			System.arraycopy(buffer, startIdx, result, 0, realLen);
			if(remove)
			{
				startIdx += realLen;
			}
		}
		//	分片取得数据的情况
		else
		{
			int frag1Len = bufferSize - startIdx;
			int frag2Len = realLen - frag1Len;
			System.arraycopy(buffer, startIdx, result, 0, frag1Len);
			System.arraycopy(buffer, 0, result, frag1Len, frag2Len);
			if(remove)
			{
				startIdx = frag2Len;
			}
		}

		//	只要去走了数据，就不可能是满队列的状态了
		if(remove)
		{
			full = false;
			if(startIdx == bufferSize)
			{
				startIdx = 0;
			}
		}
		return result;
	}

	/**
	 * 获取当前队列长度
	 */
	public synchronized int size()
	{
		if(startIdx < endIdx)
		{
			return endIdx - startIdx;
		}
		else if(startIdx > endIdx)
		{
			return buffer.length - startIdx + endIdx;
		}
		else if(full)
		{
			return bufferSize;
		}

		return 0;
	}

	/**
	 * 清空并重置缓冲区
	 */
	@SuppressWarnings("unchecked")
	public synchronized void reset()
	{
		startIdx = 0;
		endIdx = 0;
		full = false;
		buffer = (T[]) Array.newInstance(clazz, initSize);
		bufferSize = initSize;
		System.gc();
	}
}
